Topics

This topic will cover the following items 1. Scope of Visibility
2. Creating the Function foo()
3. Logical Flow Control

⚙️ Making Functions

A function is just a chunk of code, wrapped within curly brakets, and given a name.

foo <- function() {
  # R CODE GOES HERE
}

The contents of a function may be:
- A few lines of code or hundreds,
- Calls to other functions,
- Accepting variables as argument input (or not), and
- Returning some object (or not).

Variable Scope

Variables within a function are ‘localized’ to within that variable alone. This allows us to:

  1. Not run out of variable names (df and x are common ones we’ve used thus far).
  2. Not have the variables we are working with being clobbered every time we call another function.

 

Define a function with a variable x inside the function.

foo <- function() {
  x <- 42
  cat("x = ", x, "within function.\n")
}

Prior to the Function

Then examine a similarly named variable with a different assigned value before and after calling that function.

x <- 23
cat("x = ", x, "before function call.")
x =  23 before function call.

Within The Function

Within the function, the variable represents the value inside the function.

foo()
x =  42 within function.

After the Function Call

And after the function is called, examine the value of the variable.

cat("x = ", x, "after function call")
x =  23 after function call

NOTICE: before and after function are in “global variable scope, whereas inside the function, the variable is localized within the boundaries of the function itself.”

Variable Scope

The global scope is shown in RStudio on the Environment tab.

.center[]

Scope for Variables & Functions

You can also see what is in the environment by asking to list all variables and functions using the ls() function.

ls()
[1] "foo" "x"  

Function == Variable

When we make functions, we do so by assigning it the result of the call to the function() function.

Passing Varibles

While some functions do not take input variables, many require it. To pass variables into the function scope, we identify them in the

foo <- function( x ) {
  cat("x =", x, "in the function.")
}
foo( 23 )
x = 23 in the function.

Error Catching

If you do not give the function arguments it asks for, it complains loudly.

foo()
Error in foo(): argument "x" is missing, with no default

Default Values

We can put in default values for arguments by assigning values in the function parentheses.

favorite <- function( professor = "Dyer" ) {
  cat("My favorite professor is:", professor, "\n")
}

favorite()
My favorite professor is: Dyer 

Overriding Default Values

The reason to put in a default value is to make sure that you can easily accomodate everyone while minimizing the potential for errors.

favorite( professor = "Bulluck" )
My favorite professor is: Bulluck 

Getting Results

We must be explicit about wanting to send something back to the caller using the return() function.

foo <- function( name = "Alice") {
  response <- paste( name, "is in the house.")
  return( response )
}

Then we can assign the results of that function to a variable.

who_is_in_the_house <- foo()
who_is_in_the_house
[1] "Alice is in the house."

Is it Cold Outside 🌡

It is very convenient to be able to execute come code under certain conditions and another set of code under other conditions.

freezing <- function( temperature ) {
  if( temperature <= 0 ) {
    print("Brrr")
  } 
  else {
    print("Warm!")
  }
}

Testing It Out

Always verify that your code produces the correct answer, especially for boundary values.

freezing( -1 )
[1] "Brrr"
freezing( 0 )
[1] "Brrr"
freezing( 1 )
[1] "Warm!"

Multiple Conditions

Sometimes we have several different conditions that we wish to evaluate.

grade <- function( percentage ) {
  if( percentage >= 90 ) {
    return( "A" )
  } 
  else if( percentage >= 80 ) {
    return( "B" )
  }
  else if( percentage >= 70 ) {
    return( "C" )
  }
  else if( percentage >= 60 ) {
    return( "D" )
  }
  else {
    return( "F" )
  }
}

Mutipart Testing

grade( 10000000 )
[1] "A"
grade( 98 )
[1] "A"
grade( 80 )
[1] "B"
grade( 72 )
[1] "C"
grade( 54 )
[1] "F"

A Shortcut

There are so many cases where we use the dichotomous if/else workflow, that we can simplify this code

if( CONDITION ) {
  TRUE_RESPONSE
} 
else {
  FALSE_RESPONSE
}

 

ifelse( CONDITION, TRUE_RESPONSE, FALSE_RESPONSE)

Example

hour <- sample( 0:23, size = 18, replace=TRUE)
hour
 [1] 13 14  0 15  9 13 14  7 14  0  5 16  6 11 18  3 13 18

 

DayOrNight <- ifelse( hour >= 7 & hour <= 19, "Day", "Night")
DayOrNight
 [1] "Day"   "Day"   "Night" "Day"   "Day"   "Day"   "Day"   "Day"   "Day"  
[10] "Night" "Night" "Day"   "Night" "Day"   "Day"   "Night" "Day"   "Day"  

Looping

There are times when it is very helpful if we can loop over a collection of things. These may be elements in a vector, rows in a data.frame, cycling through files on your computer to load in different data components, or to manipulate and analyze tweets harvested from the internet.

Use Case

Determine which values are odd and which are even.

x <- c(2,3,4,5)
x[1] %% 2 == 0 
[1] TRUE
x[2] %% 2 == 0 
[1] FALSE
x[3] %% 2 == 0 
[1] TRUE
x[4] %% 2 == 0 
[1] FALSE

Notice: how the index number in the code is incremented by 1 for each value.

for() loop

The for() loop does this.

for( i in 1:4) { 
  cat( i, x[i] %% 2 == 0, "\n")
}
1 TRUE 
2 FALSE 
3 TRUE 
4 FALSE 

Iterating Items

Instead of using the index number to reference a value in a vector, we can refernce the set of items within the vector directly.

for( value in x ) { 
  cat( value, value %% 2 == 0, "\n")
}
2 TRUE 
3 FALSE 
4 TRUE 
5 FALSE 

Applying Functions

Once we have a function, we can then iterate over values using apply().

is_even <- function( x ) { 
  return( x %% 2 == 0)
}

Application

lapply( x, is_even )
[[1]]
[1] TRUE

[[2]]
[1] FALSE

[[3]]
[1] TRUE

[[4]]
[1] FALSE

Notice that the return is a list…

Simplifying Output

The output from lapply() is a list, whose length is the same as that of the original data. However, it is sometimes necessary to just get back a vector of values for the results instead of futzing around with a list.

Simplifying Output

Lazy programmers make small code.

sapply( x, is_even)
[1]  TRUE FALSE  TRUE FALSE

Questions

If you have any questions, please feel free to either post them as an “Issue” on your copy of this GitHub Repository, post to the Canvas discussion board for the class, or drop me an email.

Peter Sellers looking bored